<template>
  <div>

    <!-- 上传 -->
    <uploader
      v-show="progressNum <= 0"
      ref="uploader"
      :options="options"
      :auto-start="true"
      class="uploader-app"
      @file-added="onFileAdded"
      @file-progress="onFileProgress"
      @file-success="onFileSuccess"
      @complete="complete"
      @file-error="fileError"
    >
      <uploader-btn id="global-uploader-btn" ref="uploadBtn" :attrs="attrs" :style="uploadStyle">
        <i v-if="icon" :class="icon" />{{ $t(uploadBtnName) }}
      </uploader-btn>
    </uploader>

    <div v-show="progressNum > 0">
      <el-progress :text-inside="true" :stroke-width="20" :percentage="progressNum" status="success" />
    </div>

  </div>
</template>

<script>
import { ACCEPT_CONFIG } from '@/utils/config'
import { getToken } from '@/utils/auth'
import { config } from '@/utils'
import { fileBaseMerging, getVarParam } from '@/api/file/fileBase'
import { updateFileIdSingle } from '@/api/file/filefolder/folder'
import { getFileConfigByCode } from '@/api/file/fileConfig/fileConfig.js'
import SparkMD5 from 'spark-md5'
import $ from 'jquery'
import jsMd5 from 'js-md5'

export default {
      props: {
        dataType: {
          type: String,
          default: () => ''
        },
        fileId: {
          type: String,
          default: () => ''
        },
        uploadStyle: {
          type: String,
          default: () => ''
        },
        // 文件后缀: 为 '' : 环境变量,  手动填写 按填写值判断. 格式: xlx,html,css  ....
        onlySuffix: {
          type: String,
          default: () => ''
        },
        icon: {
          type: String,
          default: () => ''
        },
        uploadBtnName: {
          type: String,
          default: () => '上传'
        },
        // 文件校验配置 类型为沉淀的
        fileConfigCode: {
          type: String,
          default: () => null
        },
        // 绑定到业务ID.到底层
        businessId: {
          type: String,
          default: () => null
        },
        formDataId: {
          type: String,
          default: () => null
        },
        // 当单传按钮在table中, 返回上传的文件和id和名称时父组件需要确定把数据绑定到哪一行上
        dataTableRowNum: {
          type: Number,
          default: () => undefined
        },
        // 单文件上传
        singleFileStatus: {
          type: Boolean,
          default: () => true
        }
      },
      data() {
        return {
          options: {
            target: config.baseURL + '/file/fileBase/uploadDocLib?dataType=' + this.dataType + '&fileId=' + this.fileId,
            singleFile: this.singleFileStatus,
            chunkSize: '2048000',
            fileParameterName: 'file',
            maxChunkRetries: 3,
            testChunks: true, // 是否开启服务器分片校验
            // 服务器分片校验函数，秒传及断点续传基础
            checkChunkUploadedByResponse: function(chunk, message) {
              const objMessage = JSON.parse(message)
              if (objMessage.data !== null && objMessage.data.numbers !== null) {
                return (JSON.parse(objMessage.data.numbers) || []).indexOf(chunk.offset + 1) >= 0
              } else {
                return false
              }
            },
            headers: {
                Authorization: getToken()
            },
            query() {}
          },
          attrs: {
              accept: ACCEPT_CONFIG.getAll()
          },
          fileList: {
            fileId: '',
            fileName: ''
          },
          // 后台配置文件参数
          backgroundProfileParam: undefined,
          progressNum: 0
        }
      },
      created: function() {
        // 后台配置文件参数
        var fileVarParam = window.localStorage.getItem('backgroundProfileParam')
        if (this.isEmpty(fileVarParam)) {
          getVarParam().then(response => {
            this.backgroundProfileParam = undefined
            var suffix = response.data.document.toString() + ',' +
              response.data.image.toString() + ',' +
              response.data.media.toString() + ',' +
              response.data.text.toString()
            if (this.onlySuffix !== '' && this.onlySuffix) {
              suffix = this.onlySuffix
            }
            this.backgroundProfileParam = {
              suffix: suffix.split(','),
              maxFileSize: response.data.maxFileSize[0]
            }
            if (suffix && !this.isEmpty(suffix)) {
              this.attrs.accept = []
              var tempSuffix = []
              suffix.split(',').forEach(item => {
                tempSuffix.push('.' + item.toLowerCase())
              })
              this.attrs.accept = tempSuffix.join(',')
              this.$refs.uploadBtn.$el.querySelectorAll('input')[0].accept = tempSuffix.join(',')
            } else {
              this.attrs.accept = ACCEPT_CONFIG.getAll()
            }
            window.localStorage.setItem('backgroundProfileParam', JSON.stringify(this.backgroundProfileParam))
          })
        } else {
          this.backgroundProfileParam = JSON.parse(fileVarParam)
        }
      },
      methods: {
        // 添加文件
        onFileAdded(file) {
          const size = parseInt(this.backgroundProfileParam.maxFileSize)
          if (file.size >= 1024 * 1024 * size) {
            this.$message({ message: this.$t('请上传小于{size}MB的文件', { size: size }), type: 'error' })
            file.error = true
          }
          // 如果文件大小为0 不让上传
          if (file.size === 0) {
            this.msgError(this.$t('不能上传空文件'))
            file.error = true
          }
          // 文件类型
          const suffix = this.backgroundProfileParam.suffix
          const fileSuffix = file.name.substr(file.name.lastIndexOf('.') + 1, file.name.length)
          if (suffix.indexOf(fileSuffix.toLowerCase()) === -1) {
            this.msgError(fileSuffix + this.$t('文件格式错误, 不支持上传!'))
            file.error = true
          }
          // windows上传窗口点击确定后执行以下方法, 例如:用于 loading加载
          if (file.error === false) {
            this.$emit('okPostUploadHook', undefined)
            this.progressNum = 1
          } else {
            this.progressNum = 0
          }
          // 设置文件md5
          this.computeMD5(file)
        },
        /**
         * 计算md5，实现断点续传及秒传
         * @param file
         */
        computeMD5(file) {
          const fileReader = new FileReader()
          // const time = new Date().getTime()
          const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
          let currentChunk = 0 // 当前分片
          const chunkSize = 10 * 1024 * 1000
          const chunks = Math.ceil(file.size / chunkSize) // 计算该文件一共有多少个分片
          const spark = new SparkMD5.ArrayBuffer() // 创建一个spark md5计算arrayBuffer的对象
          // 文件状态设为"计算MD5"
          file.pause()
          loadNext() // 首次读取文件分片
          // debugger
          // 当前分片读取完成
          fileReader.onload = e => {
            // debugger
            spark.append(e.target.result)
            if (currentChunk < chunks) {
              currentChunk++
              loadNext()
              // 实时展示MD5的计算进度
              this.$nextTick(() => {
                $(`.myStatus_${file.id}`).text('校验MD5 ' + ((currentChunk / chunks) * 100).toFixed(0) + '%')
              })
            } else {
              // 最后一个分片，结束
              file.uniqueIdentifier = spark.end() + '_' + jsMd5(file.name)
              file.resume()
            }
          }
          fileReader.onerror = function() {
            this.error(this.$t('文件读取出错，请检查文件'))
            file.cancel()
          }
          function loadNext() { // 读取文件分片
            const start = currentChunk * chunkSize // 切片起始位置
            const end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize // 切片终止位置
            fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end)) // fileReader读取下一个文件分片
          }
        },
        // 上传中
        onFileProgress(file, files) {
          console.log(file, files, file.progress())
          if (file.progress() >= 0.01 && file.progress() < 1) {
            this.progressNum = parseFloat(file.progress()).toFixed(2) * 100
          }
        },
        // 上传成功
        onFileSuccess(rootFile, file, response) {
          this.fileList = []
          const res = JSON.parse(response)
          if (res.data !== null) {
            console.log(file.name + '文件上传阶段: ' + res.data.status + ',该文件存在的块数有: ' + JSON.parse(res.data.numbers))
            // 判断块是否全了. 不全的话重新上传
            if (JSON.parse(res.data.numbers).length === file.chunks.length) {
              this.callbackMerge(file)
            } else {
              console.log(file.name + '文件上传阶段: ' + res.data.status + ',该文件存在的块数有: ' + JSON.parse(res.data.numbers))
              file.bootstrap()
              file.retry()
            }
          } else {
            // 发生未知错误, 清理列表. (较大可能存在的原因:不同用户, 同一时间上传了MD5相同的附件)
            rootFile.clean()
          }
        },
        /**
         * 回调合并, 调用后台接口, 使其合并该文件
         * @param file
         */
        callbackMerge(file) {
          // 调用后台, 处理文件合并
          fileBaseMerging({
            identifier: file.uniqueIdentifier,
            dataType: this.dataType,
            totalSize: file.size,
            originalFilename: file.name,
            totalChunks: file.chunks.length,
            fileId: this.fileId
          }).then(response => {
            // 当前文件全部上传完成
            this.$bus.$emit('fileSuccess')
            // 设置文件的ID
            console.log(response)
            this.fileList.fileId = response.data.fileId
            this.fileList.fileName = response.data.fileName
            this.onFileSuccessComplete()
          }).catch(e => {
            console.error('文件合并回调失败!', e)
          })
        },
        // 传递给父组件, 并渲染到页面
        onFileSuccessComplete() {
          console.log(this.fileList)
          var fileBaseList = {}
          fileBaseList.id = this.fileList.fileId
          fileBaseList.name = this.fileList.fileName
          console.log(fileBaseList)
          // 将内容保存到底层
          var uploadFlag = true
          if (this.isNotEmpty(this.businessId)) {
            fileBaseList.businessId = this.businessId
          }
          if (this.isNotEmpty(this.formDataId)) {
            fileBaseList.dataId = this.formDataId
          }
          if (this.fileList.fileId !== '') {
            this.$emit('appendData', fileBaseList)
          }
          if (this.isNotEmpty(this.fileConfigCode)) {
            getFileConfigByCode('file_tree_' + this.fileConfigCode, 'WDCD').then(response => {
              if (response.data !== null) {
                fileBaseList.directoryId = response.data.documentType
              } else {
                uploadFlag = false
              }
              this.updateFileList(uploadFlag, fileBaseList)
            })
          } else {
            this.updateFileList(uploadFlag, fileBaseList)
          }
        },
        // 上传完成
        complete() {
        },
        fileError() {
          this.progressNum = 0
          this.msgError(this.$t('上传失败, 请检查附件是否合法!'))
        },
        // 更新刚上传的数据
        updateFileList(uploadFlag, fileBaseList) {
          if (uploadFlag) {
            updateFileIdSingle(fileBaseList).then(response => {
              console.log(response)
              this.$emit('onFileSuccess', this, this.fileList, this.dataTableRowNum)
              this.progressNum = 0
            })
          } else {
            this.progressNum = 0
          }
        },
        isNotEmpty(val) {
          return !(val === undefined || val === null || val === '')
        }
      }
    }
</script>
